﻿using System.Diagnostics.Contracts;

namespace System.Reactive.Disposables
{
	/// <summary>
	/// Provides extension methods for <see cref="SerialDisposable"/>.
	/// </summary>
	public static class SerialDisposableExtensions
	{
		/// <summary>
		/// Uses the double-indirection pattern to assign the disposable returned by the specified <paramref name="factory"/>
		/// to the <see cref="SerialDisposable.Disposable"/> property of the specified <paramref name="disposable"/>.
		/// </summary>
		/// <remarks>
		/// The double-indirection pattern avoids a race condition that can occur when the <paramref name="factory"/> 
		/// has a side-effect that causes the <see cref="SerialDisposable.Disposable"/> property of the specified 
		/// <paramref name="disposable"/> to be assigned before the <paramref name="factory"/> returns its disposable.
		/// This pattern ensures that the disposable returned by the <paramref name="factory"/> does not replace the 
		/// disposable that was assigned by the <paramref name="factory"/>.
		/// </remarks>
		/// <param name="disposable">The object to which the disposable returned by the specified <paramref name="factory"/> is assigned.</param>
		/// <param name="factory">Returns an <see cref="IDisposable"/> that is assigned to the specified <paramref name="disposable"/>.</param>
		/// <seealso href="http://social.msdn.microsoft.com/Forums/en-IE/rx/thread/4e15feae-9c4c-4962-af32-95dde1420dda#4d5fe8c8-e5e8-4ee7-93ca-b48b6a56b8af">
		/// Double indirection pattern example in Rx
		/// </seealso>
		public static void SetDisposableIndirectly(this SerialDisposable disposable, Func<IDisposable> factory)
		{
			Contract.Requires(disposable != null);
			Contract.Requires(factory != null);

			var indirection = new SingleAssignmentDisposable();

			disposable.Disposable = indirection;

			indirection.Disposable = factory();
		}
	}
}
